/* ** por compatibilidad se omiten tildes **
================================================================================
 TRABAJO PRACTICO 3 - System Programming - ORGANIZACION DE COMPUTADOR II - FCEN
================================================================================
  definicion de funciones del manejador de memoria
*/
#include "defines.h"

#include "mmu.h"
#include "i386.h"
#include "screen.h"

extern unsigned int pc[];
extern unsigned int ps[];
extern unsigned int px[];
extern void pintar();

void inicializar_mmu() {
	pintar();
}

// Ver #include "defines.h" que estan definidos los números
unsigned int inicializar_dir_tarea(unsigned int tarea) {
	unsigned int i;
	
	unsigned int offset_tarea = TAMANO_PAGINA*(tarea-1);
	unsigned int PDir_base = (PDT1 + offset_tarea);
	unsigned int PTab_base = (PTT1 + offset_tarea);
	
	unsigned char* arena_tarea = (unsigned char*)(pc[tarea-1]);
    unsigned char* codigo_tarea = (unsigned char*)(TASK1+offset_tarea);
    
    unsigned int* PPDir_base = (unsigned int*) PDir_base;
                       
    //Copiamos el codigo de la tarea a su arena asignada
    for (i=0;i<TAMANO_PAGINA;i++) {
            arena_tarea[i]=codigo_tarea[i];      
    }    
	
	//Ponemos en el indice 0 de la Page Directory apuntando a la Page Table y con valores seteados (Presente, Read/Write y Usuario)
	PPDir_base[0] = (unsigned int)((PTab_base & ~0xFFF) | 0x7);
	
	//Hacemos el identity mapping de los 2MB, Seteados con 0x3 (Presente, Read/Write)
	mapear_identity(PDir_base,3);
	
	//Mapeamos la pagina de codigo de la tarea (pc) a la direccion virtual (TASK_CODE), Seteados con 0x7 (Presente, Read/Write y Usuario)
	mapear_pagina(TASK_CODE, PDir_base, pc[tarea-1],0x7);
    
    //Mapeamos la pagina de pila de la tarea (ps) a la direccion virtual (TASK_STACK), Seteados con 0x7 (Presente, Read/Write y Usuario)
    mapear_pagina(TASK_STACK, PDir_base, ps[tarea-1],0x7);
    
    //Mapeamos la pagina de Compartida de la tarea (px) a la direccion virtual (TASK_SHARE), Seteados con 0x7 (Presente, Read/Write y Usuario)
    mapear_pagina(TASK_SHARE,PDir_base,px[tarea-1],0x7);
	
	//Devolvemos la direccion base del Page Directory
	return PDir_base;
}

void mapear_pagina(unsigned int virtual, unsigned int cr3, unsigned int fisica, unsigned int nivel) {
	unsigned int PDir_index = ((unsigned int) virtual >> 22);
	unsigned int PTab_index = (((unsigned int) virtual & 0x3FF000) >> 12);
	
	unsigned int PDir_base = ((unsigned int)(cr3 & ~0xFFF));
	
	unsigned int* PPDir_base = (unsigned int*) PDir_base;
    
    /*Para obtener el puntero a la base de la Page Table entramos en el Page Directory y vamos al indice dado por la direccion Virtual
    Corremos 12 para la izquierda y 12 para la derecha para llenar con 0 los ultimos 3 bytes, es como multiplicar el tamano_pagina */
    unsigned int* PPTab_base = (unsigned int*) ((PPDir_base[PDir_index] >> 12) << 12);

	//En el Page Directory modificamos el valor por el mismo pero seteado los atributos (Presente, Read/Write, Depende el Nivel)
	PPDir_base[PDir_index] = (unsigned int)(PPDir_base[PDir_index] | nivel);
	
	/*En el Page Table, en el indice indicado por la direccion Virtual, vamos a poner la direccio fisica en los ultimos 20 bits.
	El valor de la fisica los ultimos 3 bytes son 0 (porque son multiplos del tamano de pagina), asi que hacemos los corrimientos por si 
	no son ceros y setamos los atributos de (Presente, Read/Write, Depende el nivel)*/
	
	PPTab_base[PTab_index] = (unsigned int)(((fisica >> 12) << 12) | nivel);
	
	//Actualizamos la TLB
	tlbflush();			
}

void mapear_identity(unsigned int cr3, unsigned int nivel) {
	unsigned int i;
	
	unsigned int PDir_base = ((unsigned int)(cr3 & ~0xFFF));
	unsigned int* PPDir_base = (unsigned int*) PDir_base;
    
    unsigned int* PPTab_base = (unsigned int*) ((PPDir_base[0] >> 12) << 12);
    
	//Hacemos identity mapping en los 512 bytes (2MB) con los valores seteados de (Presente y Read/Write, Depende el nivel)	
	for (i=0;i<512;i++){
		PPTab_base[i]= (unsigned int)((i << 12) | nivel);		
	}
	
	//Actualizamos la TLB
	tlbflush();			
}

void unmapear_pagina(unsigned int virtual, unsigned int cr3){
	unsigned int PDir_index = ((unsigned int) virtual >> 22);
	unsigned int PTab_index = (((unsigned int) virtual & 0x3FF000) >> 12);
	
	unsigned int PDir_base = ((unsigned int)(cr3 & ~0xFFF));
	
	unsigned int* PPDir_base = (unsigned int*) PDir_base;
	
	/*Para obtener el puntero a la base de la Page Table entramos en el Page Directory y vamos al indice dado por la direccion Virtual
    Corremos 12 para la izquierda y 12 para la derecha para llenar con 0 los ultimos 3 bytes, es como multiplicar el tamano_pagina */
    unsigned int* PPTab_base = (unsigned int*) ((PPDir_base[PDir_index] >> 12) << 12);
    
    //En el Page Directory modificamos el valor por el mismo pero seteado los atributos (Presente, Read/Write y Usuario)
	PPTab_base[PTab_index] = (unsigned int)(PPTab_base[PTab_index] & ~0x7);
	
	//Actualizamos la TLB
	tlbflush();
}
